package cn.zswltech.gruul.biz.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.zswltech.gruul.common.dal.dao.*;
import cn.zswltech.gruul.biz.service.CustomTreeService;
import cn.zswltech.gruul.biz.service.RoleService;
import cn.zswltech.gruul.biz.service.UserService;
import cn.zswltech.gruul.biz.support.impl.IServiceImpl;
import cn.zswltech.gruul.common.constant.CustomTreeConstants;
import cn.zswltech.gruul.common.constant.OrgConstants;
import cn.zswltech.gruul.common.dto.*;
import cn.zswltech.gruul.common.entity.*;
import cn.zswltech.gruul.common.enums.StatusCodeEnum;
import cn.zswltech.gruul.common.exception.CommonErrorEnum;
import cn.zswltech.gruul.common.exception.RoleErrorEnum;
import cn.zswltech.gruul.common.exception.RoleException;
import cn.zswltech.gruul.common.result.MSG;
import cn.zswltech.gruul.common.result.Response;
import cn.zswltech.gruul.common.util.StringUtil;
import cn.zswltech.gruul.web.api.util.AccountUtil;
import cn.zswltech.gruul.web.api.util.GruulUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.util.Sqls;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


@Service("roleService")
@Slf4j
public class RoleServiceImpl extends IServiceImpl<RoleDO> implements RoleService {

    @Autowired
    private OrgDOMapper orgDOMapper;

    @Autowired
    private UserDOMapper userDOMapper;

    @Autowired
    private UserRoleDOMapper userRoleDOMapper;

    @Autowired
    private RoleResourceDOMapper roleResourceDOMapper;
    @Autowired
    private MenuDOMapper menuDOMapper;
    @Autowired
    private RoleDOMapper roleDOMapper;

    @Autowired
    private RoleMenuFunctionDOMapper roleMenuFunctionDOMapper;

    @Autowired
    private CustomTreeDOMapper customTreeDOMapper;

    @Resource
    private UserOrgRoleDOMapper userOrgRoleDOMapper;

    @Resource
    private CustomTreeService customTreeService;

    @Resource
    private ResourceDOMapper resourceDOMapper;

    @Resource
    private FunctionDOMapper functionDOMapper;

    @Resource
    private UserService userService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(RoleDelREQ req) {

        RoleDO roleDO = roleDOMapper.selectByPrimaryKey(req.getRoleId());
        if (roleDO == null){
            throw new RoleException(RoleErrorEnum.ROLE_NOT_EXISTS_ERROR);
        }
        // 删除角色
        roleDOMapper.deleteByPrimaryKey(req.getRoleId());
        // 解除用户与角色的绑定
        userOrgRoleDOMapper.deleteByExample(Example.builder(UserRoleDO.class).andWhere(Sqls.custom().andEqualTo("roleCode", roleDO.getCode())).build());
        // 角色绑定的资源组
        roleResourceDOMapper.deleteByExample(Example.builder(RoleResourceDO.class).andWhere(Sqls.custom().andEqualTo("roleCode", roleDO.getCode()).andEqualTo("systemCode",roleDO.getSystemCode() )).build());
    }

    @Override
    public List<Role2UserInfo> getRole2Uers(Role2UserREQ req) {
        String systemCode = GruulUtil.getSystemCode();
        if (systemCode == null){
            throw new RoleException(CommonErrorEnum.SYSTEM_CODE_MISS);
        }
        List<RoleDO> roleDOS = roleDOMapper.selectByExample(Example.builder(RoleDO.class).andWhere(Sqls.custom().andEqualTo("systemCode", systemCode).andIn("code", req.getRoleCodes())).build());
        if (ObjectUtil.isEmpty(roleDOS)){
            return Collections.emptyList();
        }
        Map<Long, RoleDO> roleMap = roleDOS.stream().collect(Collectors.toMap(RoleDO::getId, Function.identity()));

        List<Long> roleIds = roleDOS.stream().map(RoleDO::getId).collect(Collectors.toList());
        List<UserOrgRoleDO> userOrgRoleDOS = userOrgRoleDOMapper.selectByExample(Example.builder(UserOrgRoleDO.class).andWhere(Sqls.custom().andIn("roleId", roleIds).andEqualTo("systemCode", systemCode)).build());
        List<Long> userIds = userOrgRoleDOS.stream().map(UserOrgRoleDO::getUserId).distinct().collect(Collectors.toList());
        List<UserDO> userDOS = userDOMapper.selectByIds(userIds);
        Map<Long, UserDO> userMap = userDOS.stream().collect(Collectors.toMap(UserDO::getId, Function.identity()));

        Map<Long, List<UserOrgRoleDO>> roleUserMap = userOrgRoleDOS.stream().collect(Collectors.groupingBy(UserOrgRoleDO::getRoleId));
        Map<Long, List<Long>> roleUserIdsMap = roleUserMap.entrySet().stream().collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue().stream().map(UserOrgRoleDO::getUserId).distinct().collect(Collectors.toList())));

        List<Role2UserInfo> result = roleUserIdsMap.entrySet().stream().map(entry -> {
            Role2UserInfo role2UserInfo = new Role2UserInfo();
            role2UserInfo.setRoleCode(roleMap.get(entry.getKey()).getCode());
            role2UserInfo.setRoleName(roleMap.get(entry.getKey()).getName());

            List<Role2UserInfo.UserInfo> userInfoList = new ArrayList<>(entry.getValue().size());
            List<Long> useIdList = entry.getValue();
            for (Long userId : useIdList) {
                Role2UserInfo.UserInfo userInfo = new Role2UserInfo.UserInfo();
                userInfo.setUserId(userId);
                userInfo.setAccount(userMap.get(userId).getAccount());
                userInfo.setUserName(userMap.get(userId).getUserName());
                userInfoList.add(userInfo);
            }

            role2UserInfo.setUserInfoList(userInfoList);
            return role2UserInfo;
        }).collect(Collectors.toList());

        return result;
    }

    @Override
    public RoleMenuResourceRSP queryRoleResource(RoleMenuResourceREQ req) {
        RoleDO roleDO = roleDOMapper.selectByPrimaryKey(req.getRoleId());
        if (null == roleDO){
            throw new RoleException(RoleErrorEnum.ROLE_NOT_EXISTS_ERROR);
        }
        List<RoleResourceDO> roleResourceDOS = roleResourceDOMapper.selectByExample(Example.builder(RoleResourceDO.class).andWhere(Sqls.custom().andEqualTo("roleCode", roleDO.getCode()).andEqualTo("systemCode", roleDO.getSystemCode())).build());
        if (ObjectUtil.isEmpty(roleResourceDOS)){
            log.info("角色id:{}没有绑定的资源", req.getRoleId());
            return null;
        }
        Map<String, String> resourceCodeNameMap = roleResourceDOS.stream().collect(Collectors.toMap(RoleResourceDO::getResourceCode, RoleResourceDO::getResourceName, (k1, k2) -> k2));
        // 角色绑定的资源包
        List<String> resourceCodeList = roleResourceDOS.stream().map(RoleResourceDO::getResourceCode).distinct().collect(Collectors.toList());
        // 按资源包构建资源信息
        RoleMenuResourceRSP rsp = new RoleMenuResourceRSP();
        for (String resourceCode : resourceCodeList) {
            RoleMenuResourceRSP.SingleResource singleResource = buildSingleResource(resourceCode,resourceCodeNameMap,roleDO.getSystemCode());
            if (null != singleResource){
                rsp.add(singleResource);
            }
        }
        return rsp;
    }

    private RoleMenuResourceRSP.SingleResource buildSingleResource(String resourceCode,Map<String, String> resourceCodeNameMap,String systemCode) {
        RoleMenuResourceRSP.SingleResource singleResource = new RoleMenuResourceRSP.SingleResource();
        singleResource.setResourceCode(resourceCode);
        singleResource.setResourceName(resourceCodeNameMap.get(resourceCode));

        List<ResourceDO> resourceDOS = resourceDOMapper.selectByExample(Example.builder(ResourceDO.class).andWhere(Sqls.custom().andEqualTo("code", resourceCode)).build());
        if (ObjectUtil.isEmpty(resourceDOS)){
            log.info("角色绑定资源code：{}资源表中没有找到", resourceCode);
            return null;
        }
        Map<String, List<ResourceDO>> menuMap = resourceDOS.stream().collect(Collectors.groupingBy(ResourceDO::getMenuCode));
        List<MenuDO> menuDOList = menuDOMapper.selectByExample(Example.builder(MenuDO.class).andWhere(Sqls.custom().andEqualTo("systemCode", systemCode).andIn("code", menuMap.keySet())).build());
        Map<String, Long> menuCodeMap = menuDOList.stream().collect(Collectors.toMap(MenuDO::getCode, MenuDO::getId, (k1, k2) -> k2));
        List<ResourceViewRSP.SingleMenu> list = new ArrayList<>();
        for (String menuCode : menuMap.keySet()) {
            ResourceViewRSP.SingleMenu singleMenu = new ResourceViewRSP.SingleMenu();
            singleMenu.setMenuCode(menuCode);
            singleMenu.setMenuId(menuCodeMap.get(menuCode));
            singleMenu.setMenuName(menuMap.get(menuCode).get(0).getMenuName());
            List<ResourceViewRSP.SingleFunction> functionList = buildFunctionList(menuMap.get(menuCode));
            singleMenu.setFunctionList(functionList);
            list.add(singleMenu);
        }
        singleResource.setMenuList(list);
        return singleResource;
    }

    private List<ResourceViewRSP.SingleFunction> buildFunctionList(List<ResourceDO> resourceDOS) {
        List<String> functionCodes = resourceDOS.stream().map(ResourceDO::getFunctionCode).collect(Collectors.toList());
        List<FunctionDO> functionDOList = functionDOMapper.selectByExample(Example.builder(FunctionDO.class).andWhere(Sqls.custom().andIn("code", functionCodes)).build());
        List<ResourceViewRSP.SingleFunction> list = new ArrayList<>(functionDOList.size());
        for (FunctionDO functionDO : functionDOList) {
            ResourceViewRSP.SingleFunction singleFunction = new ResourceViewRSP.SingleFunction();
            singleFunction.setFunctionId(functionDO.getId())
                    .setFunctionCode(functionDO.getCode())
                    .setFunctionName(functionDO.getName());
            list.add(singleFunction);
        }
        return list;
    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRoleMenuFunctions(Long roleId, Long menuId, String optType, Long functionId) throws Exception {
        if (roleId == null  && menuId == null ) {
            return;
        }
        RoleMenuFunctionDO roleMenuFunctionDO = new RoleMenuFunctionDO();
        roleMenuFunctionDO.setRoleId(roleId);
        roleMenuFunctionDO.setMenuId(menuId);
        if (CustomTreeConstants.OPT_TYPE_FUNCTION.equals(optType)) {  // 删功能
            roleMenuFunctionDO.setFunctionId(functionId);
        }
        roleMenuFunctionDOMapper.deleteByRoleMenuFunction(roleMenuFunctionDO);
    }

    @Override
    public List<RoleDO> getRolesBySystemCode() {
        String systemCode = GruulUtil.getSystemCode();
        if (null == systemCode){
            throw new RoleException(CommonErrorEnum.SYSTEM_CODE_MISS);
        }
        List<RoleDO> roleDOS = roleDOMapper.selectByExample(Example.builder(RoleDO.class).andWhere(Sqls.custom().andEqualTo("systemCode", systemCode)).build());
        return roleDOS;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createRole(RoleAddREQ req) {
        String systemCode = GruulUtil.getSystemCode();
        if (null == systemCode){
            throw new RoleException(CommonErrorEnum.SYSTEM_CODE_MISS);
        }
        //查询是否存在code
        Example example = new Example(RoleDO.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("code", req.getCode());
        List<RoleDO> roleDOS = mapper.selectByExample(example);
        if (roleDOS != null && roleDOS.size() > 0) {
           throw new RoleException(RoleErrorEnum.ROLE_ADD_ERROR);
        }
        RoleDO roleDO = new RoleDO();
        roleDO.setCode(req.getCode());
        roleDO.setName(req.getName());
        roleDO.setGmtCreate(new Date());
        roleDO.setCreateBy(req.getCreateBy());
        try {
            int i = mapper.insert(roleDO);
            log.info("角色新增成功 roleId:{}",i );
        } catch (Exception e) {
            log.error(CommonErrorEnum.DB_OPERATION_ERROR.getErrMsg(),e);
            throw new RoleException(CommonErrorEnum.DB_OPERATION_ERROR);
        }
    }

    @Override
    public Response bandMenuFunction(RoleDO roleDO, Map<String, List<String>> mfs) {
        Response response = new Response();
        if (mfs == null || mfs.isEmpty()) {
            response.setCode(StatusCodeEnum.PARAM_ERROR.getStatus());
            response.setMsg(StatusCodeEnum.PARAM_ERROR.getMsg() + ":menuFunctions");
            return response;
        }
        return response;
    }

    @Override
    public Response<List<DisplayGroupDO>> getRuleMenuTrees(RoleDO rdo) {
        Response<List<DisplayGroupDO>> response = new Response<List<DisplayGroupDO>>();
        if (rdo != null) {
            RoleDO oldRule = mapper.selectOne(rdo);
            if (oldRule != null) {
                //获取角色拥有的菜单
                List<Long> menus = getRuleMenus(oldRule.getId());
                //获取角色拥有的功能
                List<Long> funcs = getRuleFunctions(oldRule.getId());
                try {
                    response = Response.success(parseOrgMenuTree(oldRule.getOrgId(), menus, funcs, false));
                } catch (Exception e) {
                    log.error("parse rule menu function tree Error:", e);
                    response.setCode(StatusCodeEnum.DATABASE_ERROR.getStatus());
                    response.setMsg(StatusCodeEnum.DATABASE_ERROR.getMsg());
                    return response;
                }
            } else {
                response.setCode(StatusCodeEnum.BUSSINESS_ERROR.getStatus());
                response.setMessage(MSG.req_error_role_not_exist);
            }
        } else {
            response.setCode(StatusCodeEnum.PARAM_ERROR.getStatus());
            response.setMessage(MSG.req_error_param_null, "RoleDO");
        }
        return response;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRuleById(RoleUpdateREQ req) {
        String systemCode = GruulUtil.getSystemCode();

        RoleDO queryRole = new RoleDO();
        queryRole.setId(req.getRoleId());
        RoleDO roleDO = mapper.selectOne(queryRole);

        if (roleDO == null) {
            throw new RoleException(RoleErrorEnum.ROLE_NOT_EXISTS_ERROR);
        }

        roleDO.setUpdateBy(req.getUpdateBy());
        roleDO.setName(req.getName());
        roleDO.setGmtModified(new Date());
        try {
            roleDOMapper.updateByPrimaryKeySelective(roleDO);
        } catch (Exception e) {
            log.error(CommonErrorEnum.DB_OPERATION_ERROR.getErrMsg(), e);
            throw new RoleException(CommonErrorEnum.DB_OPERATION_ERROR);
        }
    }


    /**
     * 根据机构构建机构菜单
     *
     * @param orgId
     * @param menus   拥有的菜单
     * @param funcs   拥有的功能
     * @return
     */
    @Override
    public List<DisplayGroupDO> parseOrgMenuTree(Long orgId, List<Long> menus, List<Long> funcs, boolean isTopLevel) {
        if (orgId == null) {
            log.error("机构id为空!");
            return null;
        }

        OrgDO orgIndb = orgDOMapper.queryByPrimaryKey(orgId);
        if (orgIndb == null) {
            log.error("机构:" + orgId + "不存在!");
            return null;
        }

        //获取机构拥有的分组
        List<CustomTreeDO> ctList = getOrgAllCustom(orgIndb, isTopLevel);
        //分组下拥有的菜单
        Map<Long, List<MenuDO>> gmMap = getOrgAllMenus(orgIndb, isTopLevel);
        //获取全部功能
        Map<Long, List<FunctionDO>> fMap = getOrgAllFunctions(orgIndb, isTopLevel);

        List<DisplayGroupDO> rsp;
        Map<Long,DisplayGroupDO> groupMap = new HashMap<>();
        for (CustomTreeDO ctDo : ctList) {
            Long groupId = ctDo.getId();
            DisplayGroupDO dg = new DisplayGroupDO();
            dg.setGroupId(groupId);
            dg.setGroupName(ctDo.getName());
            dg.setEnName(ctDo.getEnName());
            dg.setParentId(ctDo.getParentId());
            if (!gmMap.isEmpty()) {
                List<MenuDO> listMenu = gmMap.get(groupId);
                if (CollectionUtils.isEmpty(listMenu)) {
                    continue;
                }
                List<JSONObject> dmenus = new ArrayList<>();
                for (MenuDO meDo : listMenu) {
                    Long menuId = meDo.getId();
                    DisplayMenuDO dme = new DisplayMenuDO();
                    dme.setMenuName(meDo.getName());
                    dme.setMenuId(meDo.getId());
                    dme.setEnName(meDo.getEnName());
                    dme.setCode(meDo.getCode());
                    if (menus != null && menus.contains(menuId)) {
                        dme.setHasPermission(true);
                    }

                    List<DisplayFunciotnDO> dfList = new ArrayList<>();
                    List<FunctionDO> functionList = fMap.get(menuId);
                    if (functionList != null) {
                        for (FunctionDO fdo : functionList) {
                            DisplayFunciotnDO dfdo = new DisplayFunciotnDO();
                            Long fid = fdo.getId();
                            if (funcs != null && funcs.contains(fid)) {
                                dfdo.setHasPermission(true);
                            }
                            dfdo.setFuncId(fid);
                            dfdo.setName(fdo.getName());
                            dfdo.setEnName(fdo.getEnName());
                            dfdo.setCode(fdo.getCode());
                            dfList.add(dfdo);
                        }
                    }

                    dme.setFunctionList(dfList);
                    dmenus.add(JSON.parseObject(JSON.toJSONString(dme)));
                }
                dg.setChildren(dmenus);
            }
            groupMap.put(dg.getGroupId(),dg);
        }
        rsp = customTreeService.getMenu(groupMap);
        return rsp;
    }


    /**
     * 根据角色ID获取角色拥有的Menus
     *
     * @param roleId
     * @return
     */
    private List<Long> getRuleMenus(Long roleId) {
        return roleMenuFunctionDOMapper.queryMenusByRule(roleId);
    }

    /**
     * 根据角色ID获取角色拥有的functions
     *
     * @param roleId
     * @return
     */
    private List<Long> getRuleFunctions(Long roleId) {
        return roleMenuFunctionDOMapper.queryFuncsByRule(roleId);
    }

    /**
     * 获取全部功能
     *
     * @return key:menuid
     */
    private Map<Long, List<FunctionDO>> getOrgAllFunctions(OrgDO org, boolean isTopLevel) {
        Map<Long, List<FunctionDO>> rst = new HashMap<Long, List<FunctionDO>>();
        List<FunctionDO> functionDOs = new ArrayList<FunctionDO>();
        if (org.getLevel() == OrgConstants.LEVEL_1 && isTopLevel) {
            functionDOs = customTreeDOMapper.queryFuncs(null);
        } else {
            functionDOs = customTreeDOMapper.queryFuncs(org.getId());
        }
        for (FunctionDO fdo : functionDOs) {
            List<FunctionDO> fList = rst.get(fdo.getMenuId());
            if (fList == null) {
                fList = new ArrayList<>();
            }
            fList.add(fdo);
            rst.put(fdo.getMenuId(), fList);
        }
        return rst;
    }

    /**
     * 获取机构拥有菜单
     *
     * @return key:customId
     */
    private Map<Long, List<MenuDO>> getOrgAllMenus(OrgDO org, boolean orgFlag) {
        Map<Long, List<MenuDO>> rst = new HashMap();
        List<Map<String, Object>> maps = null;
        //一级机构拥有所有菜单
        if (org.getLevel() == OrgConstants.LEVEL_1 && orgFlag) {
            maps = customTreeDOMapper.queryMenuByOneLevelOrg();
        } else {
            maps = customTreeDOMapper.queryMenuByOrgId(org.getId());
        }

        for (Map<String, Object> menuMap : maps) {
            MenuDO menu = new MenuDO();
            menu.setIcon((String) getValFromMap(menuMap, "icon"));
            menu.setId((Long) getValFromMap(menuMap, "id"));
            menu.setCode((String) getValFromMap(menuMap, "code"));
            menu.setName((String) getValFromMap(menuMap, "name"));
            menu.setEnName((String) getValFromMap(menuMap, "en_name"));
            menu.setSortNo(StringUtil.obj2Integer(getValFromMap(menuMap, "sortNo")));
            Long customId = Long.valueOf(StringUtil.object2Str(getValFromMap(menuMap, "customId")));
            List<MenuDO> menus = rst.get(customId);
            if (menus == null) {
                menus = new ArrayList<MenuDO>();
            }
            menus.add(menu);
            rst.put(customId, menus);
        }
        return rst;
    }

    /**
     * 机构拥有的分组
     *
     * @param org
     * @return
     */
    private List<CustomTreeDO> getOrgAllCustom(OrgDO org, boolean orgFlag) {
        List<CustomTreeDO> customList = null;
        if (org.getLevel() == OrgConstants.LEVEL_1 && orgFlag) {
            customList = customTreeDOMapper.queryCustomByOneLevelOrg();
        } else {
            customList = customTreeDOMapper.queryCustomByOrgId(org.getId());
        }
        return customList;
    }

    /**
     * 兼容Oracle
     *
     * @param map
     * @param key
     * @return
     */
    private Object getValFromMap(Map<String, Object> map, String key) {
        Object obj = map.get(key);
        if (obj == null) {
            obj = map.get(key.toUpperCase());
        }
        return obj;
    }
}
